﻿
package extremefx.geom {
	import flash.geom.Point;	
	
	import extremefx.ICloneable;	
	import extremefx.IEquatable;	

	/**
	 * @author Marcelo Volmaro
	 */
	public class XPoint implements ICloneable, IEquatable{
		public var x:Number;
		public var y:Number;
		
		public function XPoint(... pArgs) {
			var arg0:* = pArgs[0];
			
			if (pArgs.length == 1){
				if (arg0 is Size){
					x = arg0.width;
					y = arg0.height;
					
				} else if (arg0 is XPoint || arg0 is Point){
					x = arg0.x;
					y = arg0.y;
				}
				
			} else {
				x = arg0 || 0;
				y = pArgs[1] || 0;
			}
		}
		
		public function times(pFactor:Number):XPoint{
			return new XPoint(x * pFactor, y * pFactor);
		}
		
		public function divide(pFactor:Number):XPoint{
			return new XPoint(x / pFactor, y / pFactor);
		}
		
		public function plus(pPoint:XPoint):XPoint {
			return new XPoint(x + pPoint.x, y + pPoint.y);
		}
		
		public function minus(pPoint:XPoint):XPoint {
			return new XPoint(x - pPoint.x, y - pPoint.y);
		}
		
		public function negate():XPoint {
			return new XPoint(-x, -y);
		}
		
		public function distanceTo(pPoint:XPoint):Number {
			return Math.sqrt(Math.pow(x - pPoint.x, 2) + Math.pow(y - pPoint.y, 2));
		}
		
		//return the righthand normal of this (using flash's coordinate system)
		public function normR():XPoint {
			return new XPoint(y * -1, x);
		}
		
		//convert this vector to a unit/direction vector
		public function normalize():XPoint {
			var l:Number = len();
			if (l) return new XPoint(x/l, y/l);
			return null;
		}
		
		public function proj(pV:XPoint):XPoint{
			var den:Number = pV.dot(pV);
			
			if (den){
				return times(dot(pV)/den);
			}
			 
			return XPoint(clone());//not sure how to gracefully recover but, hopefully this will be okay
		}
	
		//return the magnitude (absval) of this projected onto v2
		public function projLen(pV:XPoint):Number{
			var den:Number = pV.dot(pV);
			
			if (den){
				return Math.abs(dot(pV)/den);
				
			} else {
				return 0;
			}
		}
		
		public function dot(pV:XPoint):Number{
			return ((x * pV.x) + (y * pV.y));
		}
		
		//return the crossprod of this and v2
		//note that this is equivalent to the dotprod of this and the lefthand normal of v2
		public function cross(pV:XPoint):Number {
			return ((x * pV.y) - (y * pV.x) );
		}
		
		//return the length of this
		public function len():Number {
			return (Math.sqrt((x*x) + (y*y)));
		}
		
		public function clone():Object {
			return new XPoint(this);
		}
		
		public function equals(pObj:*):Boolean {
			if (pObj is XPoint){
				var o:XPoint = XPoint(pObj);
				return o.x == x && o.y == y;
			}
			
			return false;
		}
		
		public function toString() : String {
			return "(x:"+x+", y:"+y+")";
		}
	}
}
